#include"../../include/perspective/perspective.hpp"

using namespace DirectX;
using namespace std;

namespace SoftEngine{

    // 将点组成的物体投影到二维点，并且坐标系为GDI的，rectangle是一个命名的失败，本意为专供给demo1的。
    vector<XMFLOAT2> DotPerspective::projectRectangle(vector<XMFLOAT3> allDot){
        vector<XMFLOAT2> planeDot=Calculater::linearInterpolation(allDot,usingCamera.distance);
        Calculater::CoordinateConversion(planeDot,usingCamera.width,usingCamera.height);
        return planeDot;
    }
    DotPerspective::DotPerspective(Camera camera):usingCamera(camera){}

    bool isDotInTheTraingle(DotColorful dot,Mesh vertex,const vector<XMFLOAT3>& mesh){
        // 同侧法，即点在三个线段的同侧，如果有一个不同侧，就肯定不在这个三角形中
        // 这里的ABC为是三角形，p是需要判断的点。
        // 同侧法，需要 对 AB AP做叉乘。Bx-Ax * Py-Ay - By-Ay * Px-Ax
        auto dotA=mesh[vertex.index1];
        auto dotB=mesh[vertex.index2];
        auto dotC=mesh[vertex.index3];
        auto dotP=dot.position;
        // 方向用true来做判断
        bool directionABP=((((dotB.x-dotA.x)*(dotP.y-dotA.y)-(dotB.y-dotA.y)*(dotP.x-dotA.x)))>=0);
        bool directionBCP=((((dotC.x-dotB.x)*(dotP.y-dotB.y)-(dotC.y-dotB.y)*(dotP.x-dotB.x)))>=0);
        bool directionCAP=((((dotA.x-dotC.x)*(dotP.y-dotC.y)-(dotA.y-dotC.y)*(dotP.x-dotC.x)))>=0);
        return directionABP==directionBCP && directionBCP==directionCAP;
    }
    // 获得相交点的深度，但是实在是懒得写了，ai写的，注意！！！打个断点先
    float getDepth(DotColorful dot, Mesh vertex, const vector<XMFLOAT3>& mesh) {
        // 提取三角形顶点
        auto dotA = mesh[vertex.index1];
        auto dotB = mesh[vertex.index2];
        auto dotC = mesh[vertex.index3];
        auto dotP = dot.position;
        // 计算三角形面积 ABC
        float areaABC = abs((dotB.x - dotA.x) * (dotC.y - dotA.y) - (dotB.y - dotA.y) * (dotC.x - dotA.x));
        // 计算子三角形面积 ABP, BCP 和 CAP
        float areaABP = abs((dotB.x - dotA.x) * (dotP.y - dotA.y) - (dotB.y - dotA.y) * (dotP.x - dotA.x));
        float areaBCP = abs((dotC.x - dotB.x) * (dotP.y - dotB.y) - (dotC.y - dotB.y) * (dotP.x - dotB.x));
        float areaCAP = abs((dotA.x - dotC.x) * (dotP.y - dotC.y) - (dotA.y - dotC.y) * (dotP.x - dotC.x));
        // 根据面积比例计算 barycentric 坐标
        float alpha = areaBCP / areaABC;
        float beta = areaCAP / areaABC;
        float gamma = areaABP / areaABC;
        // 插值深度
        float depth = alpha * dotA.z + beta * dotB.z + gamma * dotC.z;
        return depth;
    }

    vector<vector<DotColorful>> MeshPerspective::projectModel(Model model){
        // 首先得到具有深度信息的所有点在投影平面上的位置,这个时候，model里面的顶点已经被转换掉了
        Calculater::linearInterpolationDepth(model.vertex,usingCamera.distance);
        // 首先进行投影，其次进行坐标轴转化，适应windowsapi
        Calculater::CoordinateConversionDepth(model.vertex,usingCamera.width,usingCamera.height);
        // 接下来由GPU进行运算，直接算得，每个像素应该展示什么东西
        vector<vector<DotColorful>> pixel(usingCamera.width, vector<DotColorful>(usingCamera.height));
        int index=0;
        int lineIndex=0;
        // 遍历每个像素
        for (auto& line :pixel){
            // 注意这里的dot是一个像素
            int dotIndex=0;
            lineIndex++;
            for (auto& dot: line){
                // 初始化
                dot.position.x=lineIndex;
                dot.position.y=dotIndex;
                dotIndex++;
                dot.color=RGB(255,255,255);
                //背景深度
                float depth=5000.0f;
                index++;
                // 遍历所有的三角形，找一找这个像素是不是在这个三角形中
                for(auto mesh: model.mesh){
                    // 这里我采用同侧法
                    if(isDotInTheTraingle(dot,mesh,model.vertex)){
                        // 计算这个像素的深度，深度越是浅，就越是优先展示，model的vertex向量中对应的XMFLOAT3的z的值就是深度
                        auto newDotDepth= getDepth(dot,mesh,model.vertex);
                        // 现在的坐标系还没有被转换，其中model.vertex的z值就是原来的y值,所以越是小就越先被人看到
                        if(newDotDepth<=depth){
                            dot.color=mesh.color;
                            depth=newDotDepth;
                        }else{
                            // 如果这个相交点的y值(计算式中的z)比较大，比目前已经有的都大，就不做处理
                            continue;
                        }
                    }else{
                        continue;;
                    }
                }
            }
        }
        return pixel;
    }
}